package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.delay.DelayMsgService;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private DelayMsgService delayMsgService;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> submitOrder(OrderInfoVo orderInfoVo) {
        // 防止订单重复提交——利用 tradeNo 实现
        Long userId = AuthContextHolder.getUserId();
        String tradeNoKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        String tradeNo = orderInfoVo.getTradeNo();
        // 利用lua脚本实现原子删除
        //1.1 构建验证流水号lua脚本
        String scriptText = "local current = redis.call('get', KEYS[1]) " +
                "if current == false then " +
                "    return true " +
                "elseif current == ARGV[1] then " +
                "    redis.call('del', KEYS[1]) " +
                "    return true " +
                "else " +
                "    return false " +
                "end";


        //2.2 执行脚本，如果脚本返回结果为false 抛出异常即可
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(scriptText, Boolean.class);
        boolean flag = (boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), orderInfoVo.getTradeNo());
        if(!flag){
            throw new GuiguException(400,"订单重复！");
        }

        // 防止订单串改
        Map<String, Object> orderInfoMap = BeanUtil.beanToMap(orderInfoVo);
        orderInfoMap.remove("payWay");
        SignHelper.checkSign(orderInfoMap);

        // 保存订单
       OrderInfo orderInfo = this.saveOrderInfo(orderInfoVo,userId);

       // 获取支付方式
        String payWay = orderInfoVo.getPayWay();
        // 余额支付
        if(SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())){
            // TODO 余额支付-远程调用账户服务扣减账户余额
            AccountLockVo accountDeductVo = new AccountLockVo();
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());
            Result deductResult = accountFeignClient.checkAndDeduct(accountDeductVo);
            if (200 != deductResult.getCode()) {
                //扣减余额失败：全局事务都需要回滚
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LESS);
            }

            // 4.2 TODO 虚拟物品发货-远程调用用户服务新增购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            Result paidRecordResult = userFeignClient.savePaidRecord(userPaidRecordVo);

            if (200 != paidRecordResult.getCode()) {
                //新增购买记录失败：全局事务都需要回滚
                throw new GuiguException(211, "新增购买记录异常");
            }
            // 4.3 订单状态：已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);
        }

        // 发送延迟消息，订单延迟关闭
        delayMsgService.senDelayMess(KafkaConstant.QUEUE_ORDER_CANCEL,orderInfo.getOrderNo(),15);


        // 返回
        HashMap<String, String> resultMap = new HashMap<>();
        resultMap.put("orderNo",orderInfo.getOrderNo());
        return resultMap;
    }

    private OrderInfo saveOrderInfo(OrderInfoVo orderInfoVo, Long userId) {
//1.保存订单
        //1.1 通过拷贝将订单VO中信息拷贝到订单PO对象中
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);
        //1.2 设置用户ID
        orderInfo.setUserId(userId);
        //1.3 为订单设置初始付款状态：未支付
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //1.4 生成全局唯一订单编号 形式：当日日期+雪花算法
        String orderNo = DateUtil.today().replaceAll("-", "") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        //1.5 保存订单
        orderInfoMapper.insert(orderInfo);
        Long orderId = orderInfo.getId();

        //2.保存订单商品明细
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollectionUtil.isNotEmpty(orderDetailVoList)) {
            orderDetailVoList.forEach(orderDetailVo -> {
                OrderDetail orderDetail = BeanUtil.copyProperties(orderDetailVo, OrderDetail.class);
                //关联订单ID
                orderDetail.setOrderId(orderId);
                orderDetailMapper.insert(orderDetail);
            });
        }

        //3.保存订单优惠明细
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (CollectionUtil.isNotEmpty(orderDerateVoList)) {
            orderDerateVoList.forEach(orderDetailVo -> {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDetailVo, OrderDerate.class);
                //关联订单ID
                orderDerate.setOrderId(orderId);
                orderDerateMapper.insert(orderDerate);
            });
        }
        //4.返回订单对象
        return orderInfo;
    }

    @Override
    public OrderInfoVo tradeOrderData(TradeVo tradeVo) {

        OrderInfoVo orderInfoVo = new OrderInfoVo();

        Long userId = AuthContextHolder.getUserId();


        orderInfoVo.setItemType(tradeVo.getItemType());

        // 原始订单金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        // 折扣金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        // 订单金额
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  声明初始化订单明细列表及订单优惠明细列表，在后续业务中向集合中新增对象
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();


        if(tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)){
            // TODO 专辑处理
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();

            //  查询用户是否购买了专辑
            Long albumId = tradeVo.getItemId();
            Boolean flage = userFeignClient.isPaidAlbum(albumId).getData();
            if(flage){
                throw new GuiguException(400,"您已经购买！");
            }
            // 查询专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();


            originalAmount = albumInfo.getPrice();
            orderAmount = originalAmount;

            // 普通折扣
            BigDecimal discount = albumInfo.getDiscount();
            if(discount.intValue() > 0){
                // 判断是否为普通用户
                if(userInfoVo.getIsVip() == 0 || userInfoVo.getIsVip() == 1 && new Date().after(userInfoVo.getVipExpireTime())){
                    // 处理金额业务
                    orderAmount = originalAmount.multiply(discount)
                            .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                }
            }
            // VIP折扣
            BigDecimal vipDiscount = albumInfo.getVipDiscount();
            if(vipDiscount.intValue() > 0){
                // 判断是否为VIP
                if (userInfoVo.getIsVip() == 1 && new Date().before(userInfoVo.getVipExpireTime())){
                    orderAmount = originalAmount.multiply(vipDiscount)
                            .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                }
            }

            // 封装数据
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            // 折扣金额
            derateAmount = originalAmount.subtract(orderAmount);
            //3.6 封装订单中优惠明细列表。判断是否有折扣
            if (derateAmount.doubleValue() > 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑优惠："+derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }
        }else if(tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)){
            // TODO 声音处理

            // 查询用户购买的声音列表
            List<TrackInfo> trackInfoList = albumFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount()).getData();

            if(CollectionUtil.isEmpty(trackInfoList)){
                throw new GuiguException(400, "无符合要求声音");
            }
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            BigDecimal price = albumInfo.getPrice();
            //4.3 计算价格 数量*单价 声音没有折扣
            originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
            orderAmount = originalAmount;
            //4.4 遍历待购买声音列表封装订单明细列表
            orderDetailVoList = trackInfoList.stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName(trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());
        }else {
            // 会员处理
            // 查询会员配置
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();

            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            derateAmount = originalAmount.subtract(orderAmount);

            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVoList.add(orderDetailVo);

            OrderDerateVo orderDerateVo = new OrderDerateVo();
            orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
            orderDerateVo.setDerateAmount(derateAmount);
            orderDerateVo.setRemarks("VIP限时优惠：" + derateAmount);
            orderDerateVoList.add(orderDerateVo);

        }

        // 封装数据
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        // 生成交易订单号
        String tradeNo = IdUtil.fastSimpleUUID();
        // 存入rdis，防止订单重复确认提交
        String tradeNoKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        redisTemplate.opsForValue().set(tradeNoKey,tradeNo,5, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);

        orderInfoVo.setTimestamp(System.currentTimeMillis());

        // 生成交易快照,防止用户篡改数据
        Map<String, Object> paramsMap = BeanUtil.beanToMap(orderInfoVo, false, true);
        orderInfoVo.setSign(SignHelper.getSign(paramsMap));

        return orderInfoVo;
    }

    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        Long userId = AuthContextHolder.getUserId();

        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo,orderNo);
        wrapper.eq(OrderInfo::getUserId,userId);
        OrderInfo orderInfo = this.getOne(wrapper);

        if(orderInfo == null) throw new GuiguException(400,"您查询的订单不存在");

        LambdaQueryWrapper<OrderDerate> orderDerateLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderDerateLambdaQueryWrapper.eq(OrderDerate::getOrderId,orderInfo.getId());
        List<OrderDerate> orderDerates = orderDerateMapper.selectList(orderDerateLambdaQueryWrapper);
        orderInfo.setOrderDerateList(orderDerates);

        LambdaQueryWrapper<OrderDetail> orderDetailLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderDetailLambdaQueryWrapper.eq(OrderDetail::getOrderId,orderInfo.getId());
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailLambdaQueryWrapper);
        orderInfo.setOrderDetailList(orderDetails);


        return orderInfo;
    }

    @Override
    public Page<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage) {
        Long userId = AuthContextHolder.getUserId();

//        Page<OrderInfo> page = this.lambdaQuery()
//                .eq(OrderInfo::getUserId, userId)
//                .page(orderInfoPage);
//
        return orderInfoMapper.findUserPage(orderInfoPage,userId);
    }

    @Override
    public void cancelOrder(String take) {
        OrderInfo orderInfo = this.lambdaQuery().eq(OrderInfo::getOrderNo, take).one();
        if(orderInfo != null && orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_UNPAID))
        {
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            this.updateById(orderInfo);
        }
    }
}





















